package com.jincp.common.utils;

import android.util.Base64;

import com.jincp.common.base.BaseApplication;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * Created by zhouweilong on 16/5/27.
 */

public class RSAUtils {
    /** */
    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /** */
    /**
     * RSA最大加密明文大小
     */
    private static final int KEY_SIZE = 1024;

    /** */
    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";

    /** */
    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /** */
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /** */
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /** */
    /**
     * <p>
     * 生成密钥对(公钥和私钥)
     * </p>
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> generateRSAKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /** */
    /**
     * <p>
     * 获取私钥
     * </p>
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64Utils.encode(key.getEncoded());
    }

    /** */
    /**
     * <p>
     * 获取公钥
     * </p>
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64Utils.encode(key.getEncoded());
    }

    private static String getPublicKey() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader
                    (BaseApplication.getInstance().getAssets().open("publicKey.keystore")));
            StringBuilder b = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                b.append(line);
            }
            return b.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }


    /**
     * 在加密解密方法的Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
     * 语句中 ； getInstance（）里面的参数如果是本地加密解密就可以直接使用keyFactory.getAlgorithm()
     * 但如果是从Java服务器端传递过来的密文要使用"RSA/None/PKCS1Padding"才能解密成功，否则会报错。
     * Android客户端使用公钥加密，在getInstance（）里面的参数要使用"RSA/ECB/PKCS1Padding"，这样Java服务器端才能解密成功！
     */


    /**
     * 使用公钥加密
     */
    public static String encryptByPublic(String content) {
        return encryptByPublic(content, getPublicKey());
    }

    /**
     * 使用公钥加密
     *
     * @param content
     * @return
     * Android客户端使用公钥加密，在getInstance（）里面的参数要使用"RSA/ECB/PKCS1Padding"，这样Java服务器端才能解密成功！
     */
    public static String encryptByPublic(String content, String publicKey) {
        String result;
        try {
            PublicKey publickey = getPublicKeyFromX509(KEY_ALGORITHM, publicKey);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publickey);
            byte plaintext[] = content.getBytes("UTF-8");
            byte[] output = cipher.doFinal(plaintext);
            result = Base64Utils.encode(output);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 使用公钥解密
     *
     * @param content 密文
     */
    public static String decryptByPublic(String content) {
        return decryptByPublic(content, getPublicKey());
    }

    /**
     * 使用公钥解密
     *
     * @param content 密文
     * @return 解密后的字符串
     * 如果是从Java服务器端传递过来的密文要使用"RSA/None/PKCS1Padding"才能解密成功，否则会报错。
     */
    public static String decryptByPublic(String content, String publicKey) {
        try {
            PublicKey publickey = getPublicKeyFromX509(KEY_ALGORITHM, publicKey);
            Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, publickey);
            InputStream ins = new ByteArrayInputStream(Base64.decode(content, Base64.DEFAULT));
            ByteArrayOutputStream writer = new ByteArrayOutputStream();
            byte[] buffer = new byte[128];
            int bufferLength;
            while ((bufferLength = ins.read(buffer)) != -1) {
                byte[] block = null;
                if (buffer.length == bufferLength) {
                    block = buffer;
                } else {
                    block = new byte[bufferLength];
                    for (int i = 0; i < bufferLength; i++) {
                        block[i] = buffer[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            return new String(writer.toByteArray(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 得到公钥
     *
     * @param algorithm
     * @param bysKey
     * @return
     */
    private static PublicKey getPublicKeyFromX509(String algorithm,
                                                  String bysKey) throws Exception {
        byte[] decodedKey = Base64.decode(bysKey, Base64.DEFAULT);
        X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePublic(x509);
    }


    /**
     * byte数组转换成16进制字符串.
     *
     * @param b b
     * @return String
     */
    public static String byte2hex(byte[] b) {
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            result.append(hex.toLowerCase());
        }
        return result.toString();
    }
}

/**
 * 在加密解密方法的Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");语句中 ； getInstance（）里面的参数如果是本地加密解密就可以直接使用keyFactory.getAlgorithm()
 * 但如果是从Java服务器端传递过来的密文要使用"RSA/None/PKCS1Padding"才能解密成功，否则会报错。
 * Android客户端使用公钥加密，在getInstance（）里面的参数要使用"RSA/ECB/PKCS1Padding"，这样Java服务器端才能解密成功！
 */

